package com.doraz.orb.methods;

import java.io.InputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Set;

import android.util.Log;
import android.webkit.URLUtil;

import com.doraz.orb.Constants;
import com.doraz.orb.structs.OrbObject;

public abstract class OrbMethod {
	protected String methodName;
	protected String path;
	protected String baseUrl;
	protected String sessionId;
	protected Hashtable<String, ArrayList<String>> params;
	protected int returnType;
	protected int statusCode;
	protected String statusMessage;
	protected String TAG = "OrbMethod";
	protected OrbObject resultObject;
	
	/**
	 * Constructs a new Method
	 * 
	 * @param methodName
	 * @param path
	 * @param returnType
	 */
	protected OrbMethod(String methodName, String path, int returnType) {
		this.methodName = methodName;
		this.path = path;
		this.baseUrl = Constants.BASE_URL;
		this.returnType = returnType;
		this.params = new Hashtable<String, ArrayList<String>>();
		this.sessionId = null;
		this.resultObject = null;
		this.statusCode = 0;
		this.statusMessage = "OK";
	}
	
	/**
	 * Gets a URL for this method
	 * 
	 * @return			the URL
	 */
	public String getURL() {
		String url = null;
		StringBuilder query = new StringBuilder();
		try {
			if(!params.isEmpty()) {
				query.append("?");
				boolean isFirst = true;
				
				if(sessionId != null) {
					addParam("sessionId", sessionId);
				}
				
				Set<String> keys = params.keySet();
				for(String key : keys) {
					//Get the key
					ArrayList<String> vals = params.get(key);
					for(int i = 0; i < vals.size(); i++) {
						if(!isFirst) {
							query.append("&");
							isFirst = false;
						}
						query.append(URLEncoder.encode(key, "UTF-8") + "="+ URLEncoder.encode(vals.get(i), "UTF-8"));
					}
				}
			}
			
			url = baseUrl + path + query.toString();
		}
		catch(Exception e) {
			Log.w(TAG, "Error Building URL: "+ baseUrl + path + query.toString());
			Log.w(TAG, "EXCEPTION: "+e.getMessage());
		}
		
		return url;
	}
	
	/**
	 * Adds a parameter
	 * 
	 * @param name	the name of the parameter
	 * @param value	the value to set to
	 */
	public void addParam(String name, String value) {
		if(!params.containsKey(name)) {
			params.put(name, new ArrayList<String>());
		}
		params.get(name).add(value);
	}
	
	/**
	 * Removes a parameter
	 * 
	 * @param name
	 */
	public void deleteParam(String name) {
		if(params.containsKey(name)) {
			params.remove(name);
		}
	}

	/**
	 * @return the methodName
	 */
	public String getMethodName() {
		return methodName;
	}

	/**
	 * @param methodName the methodName to set
	 */
	public void setMethodName(String methodName) {
		this.methodName = methodName;
	}

	/**
	 * @return the path
	 */
	public String getPath() {
		return path;
	}

	/**
	 * @param path the path to set
	 */
	public void setPath(String path) {
		this.path = path;
	}

	/**
	 * @return the returnType
	 */
	public int getReturnType() {
		return returnType;
	}

	/**
	 * @param returnType the returnType to set
	 */
	public void setReturnType(int returnType) {
		this.returnType = returnType;
	}
	
	/**
	 * @return the baseUrl
	 */
	public String getBaseUrl() {
		return baseUrl;
	}

	/**
	 * @param baseUrl the baseUrl to set
	 */
	public void setBaseUrl(String baseUrl) {
		this.baseUrl = baseUrl;
	}
	
	
	
	/**
	 * @return the resultObject
	 */
	public OrbObject getResultObject() {
		return resultObject;
	}

	/**
	 * @param resultObject the resultObject to set
	 */
	public void setResultObject(OrbObject resultObject) {
		this.resultObject = resultObject;
	}

	/**
	 * Executes the method
	 * 
	 * @return	true if successful, false otherwise
	 */
	public boolean exectute() {
		InputStream in  = null;
		try {
			in = MethodFactory.getInstance().callMethod(this);
			if(in != null) {
				this.resultObject = parseResponse(in);
				return true;
			}
			else {
				Log.w(TAG, "InputStream was null");
				return false;
			}
		}
		catch(Exception e) {
			Log.w(TAG, "EXCEPTION CALLING METHOD: "+e.getMessage());
			return false;
		}
		finally {
			if(in != null) {
				try {
					in.close();
				} catch(Exception e) {
					//Ignore
				}
			}
		}
	}

	/**
	 * Parse the Response from the OrbServer
	 * 
	 * @param in	the response input stream
	 * @return		the Object received
	 */
	public abstract OrbObject parseResponse(InputStream in);
}
